<template>
    <div v-if="os !== 'Darwin' || !configStore.device" class="audio-container">
        <div class="devices" v-if="configStore.device">
            <div class="status device-status">
                <n-icon size="28" :color="deviceColor">
                    <AudioDescription />
                </n-icon>
            </div>
            <div class="status audio-status" :class="{ active: avtive }">
                <n-icon size="18">
                    <AudioOutlined />
                </n-icon>
            </div>
        </div>
        <div style="width: 100%;" v-else>
            <n-upload ref="uploadRef" directory-dnd :max="5" @before-upload="beforeUpload" @change="handleChange">
                <n-upload-dragger>
                    <div style="margin-bottom: 12px">
                        <n-icon size="48" :depth="3">
                            <ArchiveOutline />
                        </n-icon>
                    </div>
                    <n-text style="font-size: 16px">
                        点击或者拖动文件到该区域来上传
                    </n-text>
                </n-upload-dragger>
            </n-upload>
        </div>
        <div class="review" :class="{ device: !configStore.device }">
            <n-input style="height: 100%;" v-model:value="attValue" type="textarea" placeholder="语言文本" />
        </div>
        <div class="control">
            <div class="control-button">
                <n-button v-if="configStore.device" @click="startRecording">
                    录制
                </n-button>
                <n-button v-if="configStore.device" @click="stopRecording">
                    停止
                </n-button>
                <n-button @click="palyRecording">
                    播放
                </n-button>
                <n-button @click="stopPlay">
                    停止播放
                </n-button>
                <n-button type="primary" @click="audioStt" :loading="loading">
                    发送
                </n-button>
                <n-button @click="clearTearea">
                    清空文本
                </n-button>
                <n-button @click="customConsole">
                    自定义rust接口
                </n-button>
            </div>
        </div>
    </div>
    <div v-else>
        <n-alert title="系统不支持" type="warning">
            MacOS X 暂时不支持音频转文本，如有需要请使用Windows 版本
        </n-alert>
    </div>
</template>

<script lang="ts" setup>
import { tryOnMounted } from '@vueuse/core';
import { type } from '@tauri-apps/api/os';
import { ref } from 'vue';
import { AudioDescription } from '@vicons/fa';
import { AudioOutlined } from '@vicons/antd';
import { ArchiveOutline } from '@vicons/ionicons5';
import { useMessage, type UploadFileInfo, NUpload } from 'naive-ui';
import { useConfigStore } from '@/stores/config';
import { invoke } from '@tauri-apps/api/tauri';

const uploadRef = ref<InstanceType<typeof NUpload> | null>();

const os = ref();
const deviceColor = ref('#e74141');
const avtive = ref(false);

const loading = ref(false);
const selectFile = ref<File>();
const attValue = ref()

const message = useMessage();
const configStore = useConfigStore();
// 191

const beforeUpload = async (data: { file: UploadFileInfo, fileList: UploadFileInfo[] }) => {
    console.log(data.file.file?.type)
    const type = ['mp3', 'mp4', 'mpeg', 'mpga', 'm4a', 'wav', 'awebm', 'audio/mpeg']
    if (!type.includes(data.file.file?.type!)) {
        message.error('文件类型: mp3, mp4, mpeg, mpga, m4a, wav, webm');
        return false;
    }
    uploadRef.value?.clear()
    return true
}
/**
 * 完成
 * @param options 
 */
const handleChange = (options: { file: UploadFileInfo }) => {
    selectFile.value = options.file.file!;
}
/**
 * 获取操作系统信息
 */
const getOsInfo = async () => {
    os.value = await type();
}
/**
 * 获取媒体设备
 */
const getUserAudioMedia = async () => {
    await getOsInfo();
    if (os.value !== 'Darwin' && configStore.device) {
        try {
            const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
            // 处理获取到的音频流
            handleAudioStream(stream);
            deviceColor.value = '#e74141';
        } catch (err) {
            deviceColor.value = '#a8a8a8';
            message.error('获取音频输入设备失败:' + err);
            console.error('获取音频输入设备失败:', err);
        }
    }
}
let mediaRecorder: MediaRecorder;
let audioChunks: Blob[] = [];
const handleAudioStream = async (stream: MediaStream) => {
    mediaRecorder = new MediaRecorder(stream);
    mediaRecorder.ondataavailable = (event) => {
        audioChunks.push(event.data);
    };
}
const source = ref();
/**
 * 播放
 */
const audio = ref()
function palyRecording() {
    if (mediaRecorder && configStore.device) {
        const audioBlob = new Blob(audioChunks);
        const audioUrl = URL.createObjectURL(audioBlob);
        audio.value = new Audio(audioUrl);
        // 播放预览
        audio.value.play();
    } else {
        // 读取文件
        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
        const reader = new FileReader();
        reader.onload = function (e) {
            const audioData = e.target?.result as ArrayBuffer;
            audioContext.decodeAudioData(audioData, function (buffer) {
                source.value = audioContext.createBufferSource(); // 创建一个音频源
                source.value.buffer = buffer; // 设置音频缓冲区
                source.value.connect(audioContext.destination); // 连接到输出（扬声器）
                source.value.start(0); // 播放音频
            }, function (e) {
                console.log("Audio error! ", e);
            });
        };
        reader.readAsArrayBuffer(selectFile.value!);
    }
}

const stopPlay = () => {
    if (source.value && !configStore.device) {
        source.value.stop(0)
    } else if (audio.value) {
        audio.value.pause()
        audio.value.currentTime = 0;
    }
}

/**
 * 录制
 */
function startRecording() {
    if (mediaRecorder) {
        audioChunks = [];
        mediaRecorder.start();
        avtive.value = true;
    }
}
/**
 * 停止
 */
function stopRecording() {
    if (mediaRecorder) {
        mediaRecorder.stop();
        avtive.value = false;
    }
}

/**
 * 开始转换
 */
const audioStt = async () => {
    const fromData = new FormData();
    loading.value = true;
    if (!configStore.device) {
        fromData.append('file', selectFile.value!, selectFile.value?.name)
    } else {
        const audioData = new Blob(audioChunks, { type: 'audio/mpeg' });
        fromData.append('file', audioData, 'recording.mp3')
    }
    fromData.append('model', 'whisper-1')
    // fromData.append('response_format', 'text')
    fetch('https://api.openai.com/v1/audio/transcriptions', {
        method: "POST",
        headers: {
            Authorization: "Bearer " + configStore.apikey
        },
        body: fromData
    }).then(response => {
        if (!response.ok) {
            if (response.status === 401) {
                throw new Error('请配置正确的 openai key')
            }
            if (response.status === 429) {
                throw new Error('你的请求过快，请稍后再试')
            }
            else {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
        }
        return response.json()
    })
        .then(data => {
            if (undefined == attValue.value) {
                attValue.value = data.text;
            } else {
                attValue.value = attValue.value + '\n' + data.text;
            }
        })
        .catch(error => {
            message.error(error.message)
            loading.value = false;
        })
        .finally(() => {
            loading.value = false;
        })
}

const bodyset = () => {
    const textareaDom = document.querySelector('.review .n-input__textarea-el') as HTMLTextAreaElement;
    textareaDom.readOnly = true;
}

const clearTearea = () => {
    attValue.value = '';
}

const customConsole = async () => {
    const res = await invoke('say_param', {param: '武洋'});
    alert(res);
}

tryOnMounted(() => {
    getUserAudioMedia();
    bodyset();
})
</script>

<style lang="scss">
.audio-container {
    width: 100%;
    height: 100%;
    position: relative;
    display: flex;
    flex-wrap: wrap;

    .devices {
        width: 100%;
        height: 2.4rem;
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        align-content: center;

        .status {
            margin: 0 1rem;
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            align-content: center;
        }

        .device-status {
            color: #ff9303;
        }

        .audio-status {
            color: rgb(255, 255, 255);
            background-color: #a8a8a8;
            padding: 4px 10px;
            border-radius: 4px;
        }

        .audio-status.active {
            color: rgb(255, 255, 255);
            background-color: #ff9303;
            padding: 4px 10px;
            border-radius: 4px;

            animation: audio-record 1.5s infinite;
        }
    }

    .review {
        width: 100%;
        height: calc(100% - 6.4rem);
    }

    .review.device {
        width: 100%;
        height: calc(100% - 4rem - 191px);
    }

    .control {
        width: 100%;
        height: 4rem;
        display: flex;
        flex-wrap: wrap;
        align-content: center;
        justify-content: center;
        position: relative;

        button {
            margin: 0 1rem;
        }
    }
}

@keyframes audio-record {

    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.2;
    }
}
</style>